/*
 * Amkai Enterprise Solution (AES.) 
 * Copyright 2004-2008 by Amkai(TM), Inc.
 * www.amkai.com
 * 
 * Project: webservice
 * Package: com.amkai.aes.webservice.binary.objecthandler.billing
 * File: ChargeEntryHandler.java
 * Created: 2009.02.23. 13:28:16
 * Author: polaj 
 * 
 */
package com.amkai.aes.webservice.binary.objecthandler.billing;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.amkai.aes.aescommon.billing.BillingConstants;
import com.amkai.aes.aescommon.billing.CustomChargeEntryHeaderUI;
import com.amkai.aes.aescommon.casecosting.CaseCostingHelper;
import com.amkai.aes.aescommon.casecosting.CaseCostingManager;
import com.amkai.aes.aescommon.casecosting.CaseCostingProvider;
import com.amkai.aes.aescommon.casecosting.CdmLogBillingStatus;
import com.amkai.aes.aescommon.casecosting.TMWorksheetCache;
import com.amkai.aes.aescommon.casehistory.ProcedureLogUtils;
import com.amkai.aes.aescommon.casehistory.SupplyLogUtils;
import com.amkai.aes.aescommon.dictionary.ItemManager;
import com.amkai.aes.aescommon.dictionary.SupplyResourceItemManager;
import com.amkai.aes.aescommon.exceptions.AOException;
import com.amkai.aes.aescommon.exceptions.ChargeEntryException;
import com.amkai.aes.aescommon.factory.RADBeanFactory;
import com.amkai.aes.aescommon.ledger.calculations.AnesthesiaChargeCalculationManager;
import com.amkai.aes.aescommon.ledger.managers.ChargeManager;
import com.amkai.aes.aescommon.ledger.managers.details.CptModifierUtils;
import com.amkai.aes.aescommon.ledger.managers.details.FromToManager;
import com.amkai.aes.aescommon.ledger.managers.details.ProcedureUtils;
import com.amkai.aes.aescommon.ledger.utils.SourceOfRevenueUtils;
import com.amkai.aes.aescommon.manager.DepartmentManager;
import com.amkai.aes.aescommon.organization.FacilityManager;
import com.amkai.aes.aescommon.registration.DiagnosisTableRow;
import com.amkai.aes.aescommon.registration.PmCaseUtils;
import com.amkai.aes.aescommon.schedule.AbstractAppointmentUtils;
import com.amkai.aes.aescommon.service.binary.job.billing.ChargeEntryJob;
import com.amkai.aes.aescommon.service.diee.DIEECommonUtils;
import com.amkai.aes.aescommon.service.result.billing.ChargeEntryResult;
import com.amkai.aes.aescommon.utilities.PatientUtils;
import com.amkai.aes.generatedbeans.aging.Aging;
import com.amkai.aes.generatedbeans.bill.configuration.TimeAndMaterialConfiguration;
import com.amkai.aes.generatedbeans.billinggroup.BillingGroup;
import com.amkai.aes.generatedbeans.billinggroup.BillingGroupInsurance;
import com.amkai.aes.generatedbeans.billinginterval.Batch;
import com.amkai.aes.generatedbeans.billingledger.AnesthesiaChargeCalculation;
import com.amkai.aes.generatedbeans.billingledger.Charge;
import com.amkai.aes.generatedbeans.billingledger.ChargeDiagnosisAssociation;
import com.amkai.aes.generatedbeans.casecosting.CaseCostingRowKind;
import com.amkai.aes.generatedbeans.casecosting.TimeAndMaterialWorksheetDiffs;
import com.amkai.aes.generatedbeans.casecosting.TimeAndMaterialWorksheetRow;
import com.amkai.aes.generatedbeans.cdmcasehistory.CdmCaseHistory;
import com.amkai.aes.generatedbeans.cdmcasehistory.PhaseKind;
import com.amkai.aes.generatedbeans.cdmcasehistory.logdetails.phase.AnesthesiaDepartmentLog;
import com.amkai.aes.generatedbeans.cdmcasehistory.logdetails.phase.DepartmentLog;
import com.amkai.aes.generatedbeans.cdmcasehistory.logdetails.phase.ExtendedDepartmentLog;
import com.amkai.aes.generatedbeans.cdmcasehistory.logdetails.procedure.ProcedureLog;
import com.amkai.aes.generatedbeans.cdmcasehistory.logdetails.procedure.ProcedureLogDiagnosisAssociation;
import com.amkai.aes.generatedbeans.cdmcasehistory.logdetails.resources.SupplyLog;
import com.amkai.aes.generatedbeans.dictionary.ItemKind;
import com.amkai.aes.generatedbeans.insurance.InsurancePlan;
import com.amkai.aes.generatedbeans.insurance.InsurancePolicy;
import com.amkai.aes.generatedbeans.insurance.InsurancePolicyCaseVerifiation;
import com.amkai.aes.generatedbeans.inventory.SupplyResourceCategoryMarkupRate;
import com.amkai.aes.generatedbeans.inventory.items.SupplyResourceItem;
import com.amkai.aes.generatedbeans.inventory.items.SupplyResourceMedicationItem;
import com.amkai.aes.generatedbeans.organization.Department;
import com.amkai.aes.generatedbeans.organization.DepartmentKind;
import com.amkai.aes.generatedbeans.passport.Passport;
import com.amkai.aes.generatedbeans.physstaff.Physician;
import com.amkai.aes.generatedbeans.pmcase.PmCase;
import com.amkai.aes.generatedbeans.pmcase.PmCaseDiagnosis;
import com.amkai.aes.generatedbeans.procedure.CarrierCodes;
import com.amkai.aes.generatedbeans.procedure.Procedure;
import com.amkai.aes.generatedbeans.procedure.items.RevenueCode;
import com.amkai.aes.generatedbeans.reference.PhysStaffReference;
import com.amkai.aes.generatedbeans.reference.ReferenceKind;
import com.amkai.aes.generatedbeans.registration.OrganizationSpecificProperties;
import com.amkai.aes.generatedbeans.registration.Patient;
import com.amkai.aes.generatedbeans.scheduling.AbstractAppointment;
import com.amkai.aes.generatedbeans.scheduling.AppointmentRelation;
import com.amkai.aes.generatedbeans.scheduling.ResourceConnection;
import com.amkai.aes.generatedbeans.scheduling.ResourceConnectionProperty;
import com.amkai.aes.generatedbeans.scheduling.resources.AppointmentType;
import com.amkai.aes.server.billing.AgingCalculatorManager;
import com.amkai.aes.server.billing.TimeAndMaterialServerUtils;
import com.amkai.aes.server.manager.AbstractEntryCommitter;
import com.amkai.aes.server.session.ServiceSessionManager;
import com.amkai.aes.webservice.binary.JobObjectHandler;
import com.amkai.common.extensions.hibernate.config.SiteManager;
import com.amkai.common.extensions.hibernate.config.manager.SessionManager;
import com.amkai.extensions.server.services.ServerServiceManager;
import com.jgoodies.validation.Severity;
import com.jgoodies.validation.ValidationMessage;
import com.jgoodies.validation.message.SimpleValidationMessage;

public class ChargeEntryHandler implements JobObjectHandler<ChargeEntryJob, Department, ChargeEntryResult> {

	protected static final String TITLE = "Charge Entry";
	private static final Log log = LogFactory.getLog(ChargeEntryHandler.class);

	@Override
	public ChargeEntryResult process(ChargeEntryJob job, Department department) throws Exception {
		switch (job.getKind()) {
//		case INIT_CHARGE_ENTRY:
//			return initChargeEntry(job, department);
		case GET_APPOINTMENTS_BY_CASE:
			return getAppointmentsByCase(job, department);
		case DOWNLOAD_PROCEDURE:
			return downloadProcedure(job, department);
		case DOWNLOAD_SUPPLY_RESOURCE:
			return downloadSupplyResource(job, department);
		case SAVE_CHARGES:
			ChargeEntryManager manager = new ChargeEntryManager();
			return manager.saveCharges(job, department);
		case CREATE_CHARGE:
			return createCharge(job, department);
		case IMPORT_FROM_CDM:
			return importFromCDM(job);
		case IMPORT_PROCEDURE_LOGS:
			return importProcedureLogs(job, department);
		case IMPORT_SUPPLY_LOGS:
			return importSupplyLogs(job, department);
		case SELECT_PATIENT:
			return selectPatient(job);
		}
		return null;
	}

	protected ChargeEntryResult getAppointmentsByCase(ChargeEntryJob job, Department department) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();
		SessionManager sm = SiteManager.Start();
		try {
			result.setAppointments(getAppointmentsByCase(sm, job.getPmCase(), department));
		} catch (Exception ex) {
			ex.printStackTrace(System.err);
			result.setException(ex);
		} finally {
			sm.closeSession();
		}
		return result;
	}

	protected List<AbstractAppointment> getAppointmentsByCase(SessionManager sm, PmCase pmCase, Department department) throws Exception {
		List<AbstractAppointment> l = sm
				.execute(
						"" + "FROM AbstractAppointment aa " + "LEFT JOIN FETCH aa.appointmentType at " + "LEFT JOIN FETCH aa.pmCase "
								+ "WHERE aa.pmCase=:pmcase " + "AND aa.department=:dep").setParameter("pmcase", pmCase)
				.setParameter("dep", department).list();
		for (AbstractAppointment aa : l) {
			if (aa.getFacility() != null) {
				FacilityManager.loadFacility(aa.getFacility());
			}
			for (AppointmentRelation rel : aa.getAppointmentRelations()) {
				loadAppointmentRelation(rel);
			}
			for (AppointmentRelation rel : aa.getRelatedAppointments()) {
				loadAppointmentRelation(rel);
			}
			for (ResourceConnection rc : aa.getResourceConnections()) {
				rc.getId();
				if (rc.getReservation() != null) {
					rc.getReservation().getId();
					if (rc.getReservation().getSchedulingResource() != null) {
						rc.getReservation().getSchedulingResource().getId();
						rc.getReservation().getSchedulingResource().getShortName();
						for (ResourceConnectionProperty rcp : rc.getResourceConnectionProperty()) {
							rcp.getId();
							rcp.getParentProperty();
						}
						if (rc.getReservation().getSchedulingResource().getReference() != null) {
							rc.getReservation().getSchedulingResource().getReference().getId();
							rc.getReservation().getSchedulingResource().getReference().getKind();
							if (rc.getReservation().getSchedulingResource().getReference().getKind() == ReferenceKind.PHYSSTAFF) {
								PhysStaffReference ref = sm.get(PhysStaffReference.class, rc.getReservation().getSchedulingResource().getReference()
										.getId());
								if (ref.getPhysStaff().getKind() == ItemKind.PHYSICIAN) {
									Physician p = sm.get(Physician.class, ref.getPhysStaff().getId());
									p.getName();
									p.getPerson().getDisplayName();
								}
							}
						}
					}
				}
			}
		}
		return l;
	}

	private void loadAppointmentRelation(AppointmentRelation rel) {
		if (rel.getTargetAppointment() != null) {
			if (rel.getTargetAppointment().getAppointmentType() != null)
				rel.getTargetAppointment().getAppointmentType().getName();
			FacilityManager.loadFacility(rel.getTargetAppointment().getFacility());
			for (AppointmentRelation rel1 : rel.getTargetAppointment().getAppointmentRelations()) {
				if (rel1.getOwnerAppointment() != null)
					if (rel1.getOwnerAppointment().getPmCase() != null)
						rel1.getOwnerAppointment().getPmCase().getDateOfService();
			}
		}
	}

	private ChargeEntryResult downloadProcedure(ChargeEntryJob job, Department dep) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();
		SessionManager sm = SiteManager.Start();
		try {
			Procedure procedure = sm.get(Procedure.class, job.getProcedureID());
			if (procedure != null) {
				ProcedureUtils.loadProcedure(procedure);
				result.setProcedure(procedure);
			} else
				throw new AOException(TITLE, "Procedure not found", "Procedure ID: " + job.getProcedureID(), null);
		} catch (Exception ex) {
			result.setException(ex);
			logError(ex);
		} finally {
			sm.closeSession();
		}
		return result;
	}

	private boolean hasSameRevenueCode(Charge charge, TimeAndMaterialWorksheetRow row) {
		if (charge.getRevenueCode() == null)
			return row.getRevenueCodeId() == null;
		return charge.getRevenueCode().getId().equals(row.getRevenueCodeId());
	}

	private TimeAndMaterialWorksheetRow searchTimeAndMaterialWorksheetRow(Charge charge, List<TimeAndMaterialWorksheetRow> rows) {
		for (TimeAndMaterialWorksheetRow row : rows) {
			if (charge.getProcedure() != null) {
				if (row.getKind() == CaseCostingRowKind.PROCEDURE && charge.getProcedure().getId().equals(CaseCostingHelper.getProcedureId(row))
						&& hasSameRevenueCode(charge, row) && charge.getUnits() == row.getBillableQuantity())
					return row;
			} else if (charge.getSupplyResource() != null) {
				if (row.getKind() == CaseCostingRowKind.SUPPLY_RESOURCE
						&& charge.getSupplyResource().getId().equals(CaseCostingHelper.getSupplyResourceItemId(row))
						&& hasSameRevenueCode(charge, row) && charge.getUnits() == row.getBillableQuantity())
					return row;
			}
		}
		return null;
	}

	private ChargeEntryResult downloadSupplyResource(ChargeEntryJob job, Department moduleDepartment) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();
		SessionManager sm = SiteManager.Start();
		try {
			SupplyResourceItem sri = (SupplyResourceItem) sm
					.execute(
							"FROM SupplyResourceItem sri " + "LEFT JOIN FETCH sri.sourceOfRevenue "
									+ "LEFT JOIN FETCH sri.sourceOfRevenue.journalCodeForWriteOff " + "LEFT JOIN FETCH sri.cptCode "
									+ "LEFT JOIN FETCH sri.category " + "LEFT JOIN FETCH sri.category.revenueCode " +
									// KKORNEL: AES-12394
									"LEFT JOIN FETCH sri.inventoryElement ie " + "WHERE sri.id=:id").setParameter("id", job.getSupplyResourceID())
					.uniqueResult();
			if (sri != null) {
				if (sri.getCategory() != null) {
					for (SupplyResourceCategoryMarkupRate markup : sri.getCategory().getSupplyResourceCategoryMarkupRates()) {
						markup.getAmountThrough();
					}
				}
				for (CarrierCodes cc : sri.getCarrierCodeses()) {
					if (cc.getRevenueCode() != null)
						cc.getRevenueCode().getName();
					if (cc.getAdditionalReveneuCode() != null)
						cc.getAdditionalReveneuCode().getName();
					if (cc.getCptCode() != null)
						cc.getCptCode().getName();
					CptModifierUtils.loadCptModifier(cc.getCptModifier1());
					CptModifierUtils.loadCptModifier(cc.getCptModifier2());
					CptModifierUtils.loadCptModifier(cc.getCptModifier3());
					CptModifierUtils.loadCptModifier(cc.getCptModifier4());
				}
				if (sri instanceof SupplyResourceMedicationItem) {
					SupplyResourceMedicationItem srmi = (SupplyResourceMedicationItem)sri;
					if (srmi.getUnitPerBaseMeasure() != null)
						srmi.getUnitPerBaseMeasure().getShortName();
				}
				result.setSupplyResourceItem(sri);
			} else
				throw new AOException(TITLE, "Supply Resource not found", "ID: " + job.getSupplyResourceID(), null);
		} catch (Exception ex) {
			logError(ex);
			result.setException(ex);
		} finally {
			sm.closeSession();
		}
		return result;
	}

	private ChargeEntryResult createCharge(ChargeEntryJob job, Department moduleDepartment) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();
		SessionManager sm = SiteManager.Start();
		try {
			CustomChargeEntryHeaderUI header = job.getChargeEntryHeader();
			BillingGroup selectedBillingGroup = sm.get(BillingGroup.class, job.getChargeEntryHeader().getSelectedBillingGroup().getId());
			loadBillingGroup(selectedBillingGroup);

			PmCase selectedPmCase = sm.get(PmCase.class, header.getSelectedPmCase().getId());
			if (selectedPmCase != null) {
				// KKORNEL: AES-11972
				for (CdmCaseHistory cch : selectedPmCase.getCdmCaseHistories()) {
					if (cch.getAnesthesiaDepartmentLog() != null && cch.getAnesthesiaDepartmentLog().getPhaseTimeLog() != null)
						cch.getAnesthesiaDepartmentLog().getPhaseTimeLog().getBeginDate();
				}
				if (selectedPmCase.getOrganizationSpecificProperties() != null) {
					selectedPmCase.getOrganizationSpecificProperties().getBaseOrganization().getId();
					selectedPmCase.getOrganizationSpecificProperties().getPatient().getPerson().getDisplayName();
				}
			}

			Physician renderingProvider = job.getChargeEntryHeader().getRenderingProvider();
			if (renderingProvider == null) {
				renderingProvider = selectedPmCase.getPrimaryPhysician();
				if (renderingProvider != null)
					renderingProvider.getName();
			}

			if (header.getSelectedAppointment() != null)
				AbstractAppointmentUtils.checkAppointmentBillable(header.getSelectedAppointment(), moduleDepartment);
			else
				PmCaseUtils.checkPmCaseBillable(selectedPmCase, moduleDepartment);

			// office based case
			// KKORNEL: AES-13328
			/*
			 * if (DepartmentManager.isAnestLike(moduleDepartment) &&
			 * header.getSelectedPmCase().getPrimaryCase() != null)
			 * ChargeUtils.checkPmCaseBillable(selectedPmCase);
			 */

			Passport owner = sm.get(Passport.class, ServiceSessionManager.getConnection().getRuntimePassport().getId());

			ChargeManager cm = ChargeManager.createCharge(owner, header.getSelectedPatient(), header.getSelectedBatch(), selectedBillingGroup,
					selectedPmCase, renderingProvider);
			FromToManager.loadFromTo(cm.getFromEntry());
			cm.setReferringProvider(header.getSelectedReferringProvider());
			cm.setFacility(header.getSelectedFacility());
			AbstractAppointment aa = header.getSelectedAppointment();
			cm.setAbstractAppointment(aa);
			if (aa != null) {
				// AES-14228
				// TimeZone tz =
				// TimeZone.getTimeZone(job.getModuleDepartment().getTimeZone());
				// cm.setTransactionDate(aa.getBeginTime(), tz);
				cm.setTransactionDate(new Date(aa.getBeginTime()));

				cm.setTransactionDateThru(null);
				if (aa.getAppointmentType() != null)
					cm.setTypeOfBill(aa.getAppointmentType().getTypeOfBill());
			} else {
				cm.setTransactionDate(header.getTransactionDate());
				cm.setTransactionDateThru(header.getTransactionDateThru());
			}
			cm.setTypeOfBill(header.getTypeOfBill());

			int seq = 1;
			// KKORNEL: AES-13961
			for (DiagnosisTableRow diagnosisTableRow : job.getChargeEntryHeader().getDiagnosisTableRowList()) {
				ChargeDiagnosisAssociation cda = RADBeanFactory.getInstance().createBean(ChargeDiagnosisAssociation.class);
				cda.setCharge(cm.getCharge());
				cda.setDiagnosis(diagnosisTableRow.getDiagnosis());
				cda.setSequence(seq);
				cda.setPresentOnAdmission(diagnosisTableRow.getPresentOnAdmission());
				cm.getCharge().getChargeDiagnosisAssociations().add(cda);
				seq++;
			}

			List<Charge> charges = new ArrayList<Charge>();
			charges.add(cm.getCharge());
			result.setCharges(charges);
		} catch (Exception ex) {
			result.setException(ex);
			logError(ex);
		} finally {
			sm.closeSession();
		}
		return result;
	}

	private void loadBillingGroup(BillingGroup bg) {
		if (bg != null) {
			for (BillingGroupInsurance bgi : bg.getBillingGroupInsurances()) {
				ItemManager.loadItem(bgi.getInsurancePolicy().getInsurancePlan().getDefaultPaymentJournalCode());
				ItemManager.loadItem(bgi.getInsurancePolicy().getInsurancePlan().getDefaultWriteoffJournalCode());
				ItemManager.loadItem(bgi.getInsurancePolicy().getInsurancePlan().getDefaultWriteoff2JournalCode());
				ItemManager.loadItem(bgi.getInsurancePolicy().getInsurancePlan().getDefaultDebitJournalCode());
			}
			if (bg.getPrimaryGuarantor() != null)
				bg.getPrimaryGuarantor().getDisplayName();
			if (bg.getSecondaryGuarantor() != null)
				bg.getSecondaryGuarantor().getDisplayName();
		}
	}

	protected ChargeEntryResult importFromCDM(ChargeEntryJob job) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();
		SessionManager sm = SiteManager.Start();
		try {
			Department department = sm.get(Department.class, job.getModuleDepartment().getId());

			Passport owner = sm.get(Passport.class, ServiceSessionManager.getConnection().getRuntimePassport().getId());

			Batch selectedBatch = sm.get(Batch.class, job.getChargeEntryHeader().getSelectedBatch().getId());

			PmCase selectedPmCase = sm.get(PmCase.class, job.getChargeEntryHeader().getSelectedPmCase().getId());
			PmCaseUtils.loadPmCase(selectedPmCase);

			BillingGroup selectedBillingGroup = sm.get(BillingGroup.class, job.getChargeEntryHeader().getSelectedBillingGroup().getId());
			loadBillingGroup(selectedBillingGroup);

			switch (department.getDepartmentType().getKind()) {
			case ANESTHESIA_PROFESSIONAL:
				result = importFromCDM_Anest(sm, owner, selectedBatch, selectedPmCase, selectedBillingGroup, department);
				break;
			case SURGERY_CENTER:
			case SURGICAL_HOSPITAL:
				TimeAndMaterialConfiguration configuration = TimeAndMaterialServerUtils.getTimeAndMaterialConfiguration(sm, department);
				if (configuration.isEnableTimeAndMaterialBilling()) {
					result = importFromCDMwithTM(sm, owner, selectedBatch, selectedPmCase, selectedBillingGroup, department);
				}
				else {
					result = importFromCDMwithoutTM(sm, owner, job.getChargeEntryHeader().getSelectedPatient(), selectedBatch, selectedBillingGroup, job.getChargeEntryHeader().getSelectedAppointment(), selectedPmCase, department);
				}
				break;
			}

			if (result.getCharges() != null) {
				for (Charge charge : result.getCharges()) {
					loadCharge(charge);
				}
			}
		} catch (Exception ex) {
			logError(ex);
			result.setException(ex);
		} finally {
			sm.closeSession();
		}
		return result;
	}

	private void loadCharge(Charge charge) {
		ProcedureUtils.loadProcedure(charge.getProcedure());
		SupplyResourceItemManager.loadSupplyResourceItem(charge.getSupplyResource());
		FacilityManager.loadFacility(charge.getFacility());
		ItemManager.loadItem(charge.getProvider());
		ItemManager.loadItem(charge.getReferringProvider());
		ItemManager.loadItem(charge.getSupervisingProvider());
		SourceOfRevenueUtils.loadSourceOfRevenue(charge.getSourceOfRevenue());
		ItemManager.loadItem(charge.getCptModifier_1());
		ItemManager.loadItem(charge.getCptModifier_2());
		ItemManager.loadItem(charge.getCptModifier_3());
		ItemManager.loadItem(charge.getCptModifier_4());
		ItemManager.loadItem(charge.getIcdProcedureCode1());
		ItemManager.loadItem(charge.getIcdProcedureCode2());
		ItemManager.loadItem(charge.getIcdProcedureCode3());
		AbstractAppointmentUtils.loadAbstractAppointment(charge.getAbstractAppointment());
		PmCaseUtils.loadPmCase(charge.getPmCase());
		for (ChargeDiagnosisAssociation cda : charge.getChargeDiagnosisAssociations()) {
			cda.getDiagnosis().getName();
			if (cda.getPresentOnAdmission() != null)
				cda.getPresentOnAdmission().getName();
		}
	}

	private Procedure searchProcedureForRevenueCode(SessionManager sm, RevenueCode revenueCode, Department moduleDepartment) throws Exception {
		Procedure procedure = (Procedure) sm.execute("FROM Procedure p " +
								"WHERE p.shortName=:qc " +
								"AND p.active=1 " +
								"AND p.baseOrganization.parent=:bg " +
								"AND p.baseOrganization=:bo")
									.setParameter("qc", revenueCode.getShortName())
									.setParameter("bg", revenueCode.getBaseOrganization())
									.setParameter("bo", moduleDepartment)
									.setMaxResults(1).uniqueResult();
		if (procedure == null)
			throw new AOException(TITLE, 
					"No Procedure found", 
					String.format("Item [%s] is not an active Procedure! Please add to the Procedure Dictionary to continue!", revenueCode.getShortName()), null);
		return procedure;
	}

	private ChargeEntryResult importFromCDMwithTM(SessionManager sm, Passport owner, Batch selectedBatch, PmCase selectedPmCase, BillingGroup selectedBillingGroup, Department department) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();

		CdmCaseHistory cch = PmCaseUtils.getCdmCaseHistory(selectedPmCase);
		if (cch == null)
			throw new AOException(TITLE, 
					"Case has no CDM", 
					"Case has no CDM", 
					selectedPmCase.getName());

		if (department.getDepartmentType().getKind() == DepartmentKind.SURGICAL_HOSPITAL && !cch.isReadyToBill())
			throw new AOException(TITLE, 
					"Cannot Import from CDM", 
					"Ready to Bill on CDM is not set!", null);

		CaseCostingProvider ccp = ServerServiceManager.getInstance().getCaseCostingProviderService().createCaseCostingProvider(sm, department);

		Map<Long, ChargeManager> revenueCodeCharges = new TreeMap<Long, ChargeManager>();

		Set<TimeAndMaterialWorksheetRow> rows = ccp.getTimeAndMaterialWorksheetDiffs(cch, true, true, null).getUnbilledRows();

		List<TimeAndMaterialWorksheetRow> l = sortTimeAndMaterialWorksheetRows(rows);
		
		log.debug(String.format("Found %d row(s) unbilled on Time & Material Worksheet", l.size()));
		int c = 0;
		for (TimeAndMaterialWorksheetRow row : l) {

			c++;
			log.debug(String.format("%d. row: %s %s", c, row.getKind(), row.getName()));
			if (BigDecimal.ZERO.compareTo(row.getTotalValue()) == 0) {
				continue;
			}
			ChargeManager cm = null;
			if (row.getCptCodeId() == null) {
				if (row.getRevenueCodeId() == null) {
					result.addMessage(String.format("No Revenue Code found for %s [%s]", row.getKind(), row.getName()));
					continue;
				}
				cm = revenueCodeCharges.get(row.getRevenueCodeId());
				if (cm == null) {
					cm = ChargeManager.createCharge(owner, cch.getPatient(), selectedBatch, selectedBillingGroup, selectedPmCase, cch.getSurgeon());
					
					//AES-16358
					cm.setTransactionDate(row.getTransactionDate());
					cm.setTransactionDateThru(row.getTransactionDateThru());
					cm.setUnits(row.getBillableQuantity());
					cm.enableChangeUnits(false);
					cm.setAmount(row.getTotalValue());
					cm.enableChangeAmount(false);
					AbstractAppointment aa = cch.getPmCase().getAbstractAppointment();
					cm.setFacility(cch.getFacility());
					if (cm.getFacility() == null)
						cm.setFacility(cm.getPmCase().getAbstractAppointment().getFacility());
					cm.setAbstractAppointment(aa);
					cm.setRenderingProvider(cch.getSurgeon());

					AppointmentType at = cch.getAppointmentType();
					if (at == null)
						at = aa.getAppointmentType();

					cm.setTypeOfBill(at.getTypeOfBill());
					RevenueCode revenueCode = sm.get(RevenueCode.class, row.getRevenueCodeId());
					Procedure procedure = searchProcedureForRevenueCode(sm, revenueCode, department);
					cm.setProcedure(procedure);
					cm.setRevenueCode(revenueCode);
					cm.setSourceOfRevenue(procedure.getSourceOfRevenue());
					result.addCharge(cm.getCharge());
					revenueCodeCharges.put(row.getRevenueCodeId(), cm);
				} else {
					cm.setUnits(cm.getCharge().getUnits() + row.getBillableQuantity());
					cm.setAmount(cm.getAmount().add(row.getTotalValue()));
					if (row.getTransactionDate() != null) {
						if (cm.getTransactionDate() == null || cm.getTransactionDate().after(row.getTransactionDate()))
							cm.setTransactionDate(row.getTransactionDate());
						
					}
					if (row.getTransactionDateThru() != null) {
						if (cm.getTransactionDateThru() == null || cm.getTransactionDateThru().before(row.getTransactionDateThru()))
							cm.setTransactionDateThru(row.getTransactionDateThru());
						
					}
				}
				if (row.getProcedureLogId() != null) {
					ProcedureLog procedureLog = sm.get(ProcedureLog.class, row.getProcedureLogId());
					List<ProcedureLogDiagnosisAssociation> diags = new ArrayList<ProcedureLogDiagnosisAssociation>(procedureLog.getProcedureLogDiagnosisAssociations());
					Collections.sort(diags, new Comparator<ProcedureLogDiagnosisAssociation>() {

						@Override
						public int compare(ProcedureLogDiagnosisAssociation o1,	ProcedureLogDiagnosisAssociation o2) {
							return o1.getSequence()-o2.getSequence();
						}
					});
					
					int seq = 1;
					for (ProcedureLogDiagnosisAssociation association : diags) {

						ChargeDiagnosisAssociation cda = RADBeanFactory.getInstance().createBean(ChargeDiagnosisAssociation.class);
						cda.setDiagnosis(association.getDiagnosis());
						cda.setCharge(cm.getCharge());
						cda.setSequence(seq);
						// KKORNEL: AES-13961
						cda.setPresentOnAdmission(association.getPresentOnAdmission());
						cm.getCharge().getChargeDiagnosisAssociations().add(cda);
						seq++;
					}
				}				
			} else { // has CPT Code
				switch (row.getKind()) {
				case PROCEDURE:
					ProcedureLog procedureLog = sm.get(ProcedureLog.class, row.getProcedureLogId());
					cm = ChargeManager.createChargeFromProcedureLog(owner, selectedBatch, selectedBillingGroup, procedureLog);
					result.addCharge(cm.getCharge());
					break;
				case SUPPLY_RESOURCE:
				case TRAY_ITEM:
					SupplyLog supplyLog = sm.get(SupplyLog.class, row.getSupplyLogId());
					cm = ChargeManager.createChargeFromSupplyLog(owner, selectedBatch, selectedBillingGroup, supplyLog, row.getBillableQuantity());
					cm.setRenderingProvider(cch.getSurgeon());
					result.addCharge(cm.getCharge());
					break;
				}
				//AES-16358
				if (cm != null) {
					cm.setUnits(row.getBillableQuantity());
					cm.enableChangeUnits(false);
					cm.setAmount(row.getTotalValue());
					cm.enableChangeAmount(false);
					cm.setTransactionDate(row.getTransactionDate());
					cm.setTransactionDateThru(row.getTransactionDateThru());
				}
			}
		}//for

		rows = ccp.getTimeAndMaterialWorksheetDiffs(cch, true, true, null).getOverbilledRows();
		for (TimeAndMaterialWorksheetRow row : rows) {
			result.addMessage(String.format("%s [%s/%s] with [%.1f x " + BillingConstants.CURRENCY_SYMBOL + "%.2f] is overbilled", row.getKind(),
					row.getShortName(), row.getName(), row.getBillableQuantity(), row.getTotalValue()));
		}

		return result;

	}

	private List<TimeAndMaterialWorksheetRow> sortTimeAndMaterialWorksheetRows(Set<TimeAndMaterialWorksheetRow> rows) {
		List<TimeAndMaterialWorksheetRow> l = new ArrayList<TimeAndMaterialWorksheetRow>();
		l.addAll(rows);
		Collections.sort(l, new Comparator<TimeAndMaterialWorksheetRow>() {

			@Override
			public int compare(TimeAndMaterialWorksheetRow row1, TimeAndMaterialWorksheetRow row2) {
				return DIEECommonUtils.compare(row1.getSequence(), row2.getSequence());
			}
		});
		return l;
	}
	
	private ChargeEntryResult importFromCDMwithoutTM(SessionManager sm, Passport owner, Patient patient, Batch batch, BillingGroup billingGroup, AbstractAppointment selectedAppointment, PmCase pmCase, Department department) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();

		PmCaseUtils.loadPmCase(pmCase);
		CdmCaseHistory cch = PmCaseUtils.getCdmCaseHistory(pmCase);
		if (cch == null)
			throw new AOException(TITLE, "PmCase has no CDM", "PmCase has no CDM", pmCase.getName());

		CaseCostingProvider ccp = ServerServiceManager.getInstance().getCaseCostingProviderService().createCaseCostingProvider(sm, department);

		Set<TimeAndMaterialWorksheetRow> rows = ccp.getTimeAndMaterialWorksheetDiffs(cch, true, true, null).getUnbilledRows();
		
		List<TimeAndMaterialWorksheetRow> l = sortTimeAndMaterialWorksheetRows(rows);
		
		log.debug(String.format("Found %d row(s) unbilled on Time & Material Worksheet", l.size()));
		int c = 1;
		for (TimeAndMaterialWorksheetRow row : l) {

			log.debug(String.format("%d. row: %s", c, row.getKind()));
			// AES-16654
			// import only the logs belong to the selected appointment
			if (!selectedAppointment.getId().equals(row.getAppointmentId())) {
				continue;
			}
			if (row.getBillableQuantity() == 0.0) {
				continue;
			}
			ChargeManager cm = null;
			switch (row.getKind()) {
			case PROCEDURE:
				ProcedureLog procedureLog = sm.get(ProcedureLog.class, row.getProcedureLogId());
				cm = ChargeManager.createChargeFromProcedureLog(owner, batch, billingGroup, procedureLog);
				cm.setUnits(row.getBillableQuantity());
				break;
			case SUPPLY_RESOURCE:
			case TRAY_ITEM:
				SupplyLog supplyLog = sm.get(SupplyLog.class, row.getSupplyLogId());
				cm = ChargeManager.createChargeFromSupplyLog(owner, batch, billingGroup, supplyLog, row.getBillableQuantity());
				cm.setRenderingProvider(cch.getSurgeon());
				break;
			}
			if (cm != null) {
				cm.setAbstractAppointment(selectedAppointment);
				cm.setAmount(row.getTotalValue());
				cm.enableChangeAmount(false);
				cm.enableChangeUnits(false);
				result.addCharge(cm.getCharge());
			}
			c++;
		}
		return result;
	}

	private ChargeEntryResult importFromCDM_Anest(SessionManager sm, Passport owner, Batch selectedBatch, PmCase selectedPmCase, BillingGroup selectedBillingGroup, Department department) throws Exception {

		ChargeEntryResult result = new ChargeEntryResult();

		PmCaseUtils.loadPmCase(selectedPmCase);
		CdmCaseHistory cch = PmCaseUtils.getCdmCaseHistory(selectedPmCase);
		if (cch == null)
			throw new AOException(TITLE, "PmCase has no CDM", "PmCase has no CDM", selectedPmCase.getName());

		CaseCostingProvider ccp = ServerServiceManager.getInstance().getCaseCostingProviderService().createCaseCostingProvider(sm, department);

		Set<TimeAndMaterialWorksheetRow> rows = ccp.getTimeAndMaterialWorksheetDiffs(cch, true, true, null).getUnbilledRows();
		
		List<TimeAndMaterialWorksheetRow> l = sortTimeAndMaterialWorksheetRows(rows);
		
		log.debug(String.format("Found %d row(s) unbilled", l.size()));
		int c = 1;
		for (TimeAndMaterialWorksheetRow row : l) {

			log.debug(String.format("%d. row: %s", c, row.getKind()));
			if (row.getBillableQuantity() == 0.0) {
				switch (row.getKind()) {
				default:
					continue;
				}
			}
			switch (row.getKind()) {
			case PROCEDURE:
				ProcedureLog procedureLog = sm.get(ProcedureLog.class, row.getProcedureLogId());
				ChargeManager cm = createChargeFromProcedureLog_Anest(procedureLog, owner, selectedBatch, selectedBillingGroup, department);
				cm.setUnits(row.getBillableQuantity());
				cm.setAmount(row.getTotalValue());
				result.addCharge(cm.getCharge());
				break;
			}
			c++;
		}
		return result;
	}

	private ChargeEntryResult importProcedureLogs(ChargeEntryJob job, Department dep) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();
		SessionManager sm = SiteManager.Start();
		try {
			Passport owner = sm.get(Passport.class, ServiceSessionManager.getConnection().getRuntimePassport().getId());

			BillingGroup selectedBillingGroup = sm.get(BillingGroup.class, job.getChargeEntryHeader().getSelectedBillingGroup().getId());
			loadBillingGroup(selectedBillingGroup);

			for (ProcedureLog pl : job.getProcedureLogs()) {
				ProcedureLog procedureLog = sm.get(ProcedureLog.class, pl.getId());
				ProcedureLogUtils.loadProcedureLog(procedureLog);
				if (DepartmentManager.isSCLike(dep)) {
					result.addCharge(createChargeFromProcedureLog_SC(procedureLog, owner, job.getChargeEntryHeader().getSelectedBatch(), selectedBillingGroup).getCharge());
				} else if (DepartmentManager.isAnestLike(dep)) {
					Department moduleDepartment = sm.get(Department.class, dep.getId());
					result.addCharge(createChargeFromProcedureLog_Anest(procedureLog, owner, job.getChargeEntryHeader().getSelectedBatch(),	selectedBillingGroup, moduleDepartment).getCharge());
				}
			}
			if (result.getCharges() != null) {
				for (Charge charge : result.getCharges()) {
					loadCharge(charge);
				}
			}
		} catch (Exception ex) {
			logError(ex);
			result.setException(ex);
		} finally {
			sm.closeSession();
		}
		return result;
	}

	private ChargeEntryResult importSupplyLogs(ChargeEntryJob job, Department department) throws Exception {
		ChargeEntryResult result = new ChargeEntryResult();
		SessionManager sm = SiteManager.Start();
		try {
			Passport owner = sm.get(Passport.class, ServiceSessionManager.getConnection().getRuntimePassport().getId());
			BillingGroup selectedBillingGroup = sm.get(BillingGroup.class, job.getChargeEntryHeader().getSelectedBillingGroup().getId());
			loadBillingGroup(selectedBillingGroup);
			Batch selectedBatch = sm.get(Batch.class, job.getChargeEntryHeader().getSelectedBatch().getId());
			List<SupplyLog> supplyLogs = new ArrayList<SupplyLog>();
			SupplyLog supplyLog = null;
			Map<Long, TMWorksheetCache> cache = new TreeMap<Long, TMWorksheetCache>();
			CaseCostingProvider ccp = ServerServiceManager.getInstance().getCaseCostingProviderService().createCaseCostingProvider(sm, department);
			CaseCostingManager ccm = new CaseCostingManager(ccp, department);
			TMWorksheetCache tmwCache = null;
			ChargeManager cm = null;
			for (SupplyLog sl : job.getSupplyLogs()) {

				supplyLog = sm.get(SupplyLog.class, sl.getId());
				tmwCache = cache.get(supplyLog.getDepartmentLog().getCdmCaseHistory().getId());
				if (tmwCache == null) {
					tmwCache = ccm.getTMWorksheetCache(supplyLog.getDepartmentLog().getCdmCaseHistory(), true, true);
					cache.put(supplyLog.getCdmCaseHistory().getId(), tmwCache);
				}

				CdmLogBillingStatus status =  tmwCache.getBillingStatusFor(supplyLog);
				//double units = SupplyLogUtils.calculateSupplyLogUnits(supplyLog) - status.getUnitsBilled();
				double units = status.getUnitsCdm() - status.getUnitsBilled();
				if (units > 0.0) {
					cm = ChargeManager.createChargeFromSupplyLog(owner, selectedBatch, selectedBillingGroup, supplyLog, units);
					// AES-16703
					if (supplyLog.getDepartmentLog() != null) {
						DepartmentLog departmentLog = sm.get(DepartmentLog.class, supplyLog.getDepartmentLog().getId());
						if (departmentLog != null && (departmentLog instanceof ExtendedDepartmentLog)) {
							cm.setAbstractAppointment(((ExtendedDepartmentLog)departmentLog).getAbstractAppointment());
						}
					}
					
					// Put this SupplyLogID to charge to force the synchronization with this SupplyLog
					cm.getCharge().putTranzientProperty(AbstractEntryCommitter.SUPPLY_LOG_ID, supplyLog.getId());
					BigDecimal amount = status.getAmountCdm().subtract(status.getAmountBilled());
					
					/*
					if (supplyLog.getSupplyResourceItem().isSerialized() || supplyLog.getSupplyResourceItem().isExpiration()) {
						
						BigDecimal amount = BigDecimal.ZERO;
						for (AdjustmentDescriptor ad : supplyLog.getAdjustmentDescriptors()) {
							for (TransactionDescriptor td : ad.getTransactionDescriptors()) {
								amount = amount.add(td.getUnitCost().multiply(new BigDecimal(td.getQuantity())));
							}
						}
						cm.setAmount(amount);
						cm.setDue(amount);
					}
					*/
					cm.setAmount(amount);
					cm.setDue(amount);
					loadCharge(cm.getCharge());
					for (CdmCaseHistory cch : cm.getPmCase().getCdmCaseHistories()) {
						cch.isReadyToBill();
					}
					result.addCharge(cm.getCharge());
				}
				supplyLogs.add(supplyLog);
				SupplyLogUtils.loadSupplyLog(supplyLog);
			}
			if (result.getCharges() != null) {
				for (Charge charge : result.getCharges()) {
					loadCharge(charge);
				}
			}
		} catch (Exception ex) {
			logError(ex);
			result.setException(ex);
		} finally {
			sm.closeSession();
		}
		return result;
	}

	private ChargeManager createChargeFromProcedureLog_SC(ProcedureLog procedureLog, Passport owner, Batch selectedBatch,
			BillingGroup selectedBillingGroup) throws Exception {

		ProcedureLogUtils.loadProcedureLog(procedureLog);
		ProcedureUtils.loadProcedure(procedureLog.getProcedure());
		ChargeManager cm = ChargeManager.createChargeFromProcedureLog(owner, selectedBatch, selectedBillingGroup, procedureLog);
		
		// Put this ProcedureLogID to charge to force the synchronization with this ProcedureLog
		cm.getCharge().putTranzientProperty(AbstractEntryCommitter.PROCEDURE_LOG_ID, procedureLog.getId());
		// AES-6594
		cm.setSupervisingProvider(null);
		loadCharge(cm.getCharge());
		for (CdmCaseHistory cch : cm.getPmCase().getCdmCaseHistories()) {
			cch.isReadyToBill();
		}
		return cm;
	}

	private ChargeManager createChargeFromProcedureLog_Anest(ProcedureLog procedureLog, Passport owner, Batch selectedBatch, BillingGroup selectedBillingGroup, Department department) throws Exception {
		ProcedureLogUtils.loadProcedureLog(procedureLog);
		ProcedureUtils.loadProcedure(procedureLog.getProcedure());
		ChargeManager cm = ChargeManager.createChargeFromProcedureLog(owner, selectedBatch, selectedBillingGroup, procedureLog);
		
		// Put this ProcedureLogID to charge to force the synchronization with this ProcedureLog
		cm.getCharge().putTranzientProperty(AbstractEntryCommitter.PROCEDURE_LOG_ID, procedureLog.getId());
		// KKORNEL: AES-11972
		AnesthesiaDepartmentLog anesthesiaDepartmentLog = null;
		for (CdmCaseHistory cch : cm.getPmCase().getCdmCaseHistories()) {
			anesthesiaDepartmentLog = cch.getAnesthesiaDepartmentLog();
			if (anesthesiaDepartmentLog != null) {
				if (anesthesiaDepartmentLog.getPhaseTimeLog() != null) {
					anesthesiaDepartmentLog.getPhaseTimeLog().getBeginDate();
				}
			}
		}
		InsurancePlan plan = null;
		switch (cm.getFromEntry().getKind()) {
		case PRIMARY_INSURANCE:
		case SECONDARY_INSURANCE:
		case TERTIARY_INSURANCE:
			plan = cm.getFromEntry().getInsurancePolicy().getInsurancePlan();
		}

		AnesthesiaChargeCalculationManager manager = new AnesthesiaChargeCalculationManager(cm.getProcedure(), cm.getRenderingProvider()
				.getSpecialty(), plan, department, anesthesiaDepartmentLog);
		AnesthesiaChargeCalculation anesthesiaChargeCalculation = null;
		if (manager != null) {
			anesthesiaChargeCalculation = manager.getAnesthesiaChargeCalculation();
			if (anesthesiaChargeCalculation != null && anesthesiaChargeCalculation.getAnesthesiaRevenueFormula() != null) {
				anesthesiaChargeCalculation.getAnesthesiaRevenueFormula().getName();
			}
			if (anesthesiaDepartmentLog != null) {
				cm.getCharge().setAnesthesiaServiceType(anesthesiaDepartmentLog.getAnesthesiaServiceType());
			}
			cm.getCharge().setAnesthesiaChargeCalculation(anesthesiaChargeCalculation);
		}
		loadCharge(cm.getCharge());
		for (CdmCaseHistory cch : cm.getPmCase().getCdmCaseHistories()) {
			cch.isReadyToBill();
		}
		return cm;
	}

	class ChargeEntryManager extends AbstractEntryCommitter {

		private ChargeEntryResult saveCharges(ChargeEntryJob job, Department moduleDepartment) throws Exception {
			ChargeEntryResult result = new ChargeEntryResult();
			SessionManager sm = SiteManager.Start();
			try {
				Passport owner = sm.get(Passport.class, ServiceSessionManager.getConnection().getRuntimePassport().getId());

				List<ValidationMessage> validationMessages = new ArrayList<ValidationMessage>();
				boolean hasError = false;
				if (DepartmentManager.isSCLike(moduleDepartment)) {
					
					TimeAndMaterialConfiguration configuration = TimeAndMaterialServerUtils.getTimeAndMaterialConfiguration(sm, moduleDepartment);
					if (configuration.isEnableTimeAndMaterialBilling()) {
					
						CdmCaseHistory cch = null;
						if (job.getCharges().size() > 0) {
							cch = searchCdmCaseHistory(sm, job.getCharges().get(0).getPatient(), job.getCharges().get(0).getPmCase());
							if (cch == null)
								throw new Exception(String.format("Case [%s] has no CDM!", job.getCharges().get(0).getPmCase().getName()));
						}
						CaseCostingProvider ccp = ServerServiceManager.getInstance().getCaseCostingProviderService().createCaseCostingProvider(sm, moduleDepartment);
	
						TimeAndMaterialWorksheetDiffs diffs = ccp.getTimeAndMaterialWorksheetDiffs(cch, true, true, job.getCharges());
						for (TimeAndMaterialWorksheetRow row : diffs.getUnbilledRows()) {
							if (row.getBillableQuantity() != 0) {
								ValidationMessage warningMessage = new SimpleValidationMessage(String.format("%s [%s/%s] with [%.2f x " + BillingConstants.CURRENCY_SYMBOL + "%.2f] is unbilled", row.getKind(), row.getShortName(), row.getName(), row.getBillableQuantity(), row.getTotalValue()), Severity.WARNING); 
								validationMessages.add(warningMessage);
							}
						}
	
						for (TimeAndMaterialWorksheetRow row : diffs.getOverbilledRows()) {
							if (row.getBillableQuantity() != 0) {
								validationMessages.add(new SimpleValidationMessage(String.format("%s [%s/%s] with [%.2f x " + BillingConstants.CURRENCY_SYMBOL + "%.2f] is overbilled",	row.getKind(), row.getShortName(), row.getName(), row.getBillableQuantity(), row.getTotalValue()), Severity.ERROR));
								hasError = true;
							}
						}
	
						if (hasError)
							throw new ChargeEntryException(TITLE, "Time & Material Worksheet Check", validationMessages);						
					}
				}
				sm.openTransaction();

				//BigDecimal totalAmount = BigDecimal.ZERO;
				for (Charge charge : job.getCharges()) {
					try {
						saveEntry(sm, true, charge, moduleDepartment, owner);
						//totalAmount = totalAmount.add(charge.getAmount());
					}
					catch (Exception ex) {
						validationMessages.add(new SimpleValidationMessage(ex.getMessage(), Severity.ERROR));
						logError(ex);
						hasError = true;
					}
				}
				recalculateFields(sm, moduleDepartment);

				if (!hasError) {
					result.setCharges(job.getCharges());
					sm.commitTransaction();
				} else
					throw new ChargeEntryException(TITLE, "Saving Charges", validationMessages);

				if (job.getCharges().size() > 0)
					result.setHasUnassignedPayments(LedgerHandler.getActiveOpenUnassignedPaymentsByPatientAndCase(sm,
							job.getCharges().get(0).getPatient().getId(), job.getCharges().get(0).getPmCase().getId(), moduleDepartment).size() > 0);
				
				if (validationMessages.size() > 0)
					result.setValidationMessages(validationMessages);
				
			} catch (Exception ex) {
				sm.rollbackTransaction();
				if (!(ex instanceof AOException))
					logError(ex);
				result.setException(ex);
			} finally {
				sm.closeSession();
			}
			return result;
		}
	}
	
	private ChargeEntryResult selectPatient(ChargeEntryJob job) throws Exception {
		long start = System.currentTimeMillis();
		ChargeEntryResult result = new ChargeEntryResult();
		SessionManager sm = SiteManager.Start();
		try {
			Patient patient = (Patient)sm.execute("FROM Patient pa " +
					"JOIN FETCH pa.person pe " +
					"JOIN FETCH pa.account " +
					"JOIN FETCH pa.organizationSpecificPropertieses osp " +
					"LEFT JOIN FETCH osp.pmCases cases " +
					"LEFT JOIN FETCH cases.billingGroups bg " +
					"LEFT JOIN FETCH bg.billingGroupInsurances bgi " +
					"LEFT JOIN FETCH bgi.insurancePolicy ip " +
					// KKORNEL: AES-11846
					"LEFT JOIN FETCH ip.insuranceClaimOffice ico " + 
					"LEFT JOIN FETCH ico.location icoloc " +
					"LEFT JOIN FETCH icoloc.zipCode icoloczc " + 
					"LEFT JOIN FETCH icoloczc.city icoloczccity " +
					"LEFT JOIN FETCH icoloczc.zip icoloczczip " + 
					"LEFT JOIN FETCH icoloczczip.state icoloczczipstate " +
					"LEFT JOIN FETCH icoloc.fax icolocfax " + 
					"LEFT JOIN FETCH icoloc.phone icolocphone " +
					"LEFT JOIN FETCH ico.insuranceCarrier iCarr " + 
					"LEFT JOIN FETCH ip.insurancePlan plan " +
					"WHERE pa.id=:patient_id")
						.setParameter("patient_id", job.getPatientID())
						.uniqueResult();

			Set<PmCase> cases = PatientUtils.getPmCases(patient, job.getModuleDepartment());
			if (cases == null || cases.size() == 0) {
				throw new AOException(TITLE, "Patient has no Case", String.format("%s has no Case!", patient.getPerson().getDisplayName()), null);
			}

			// FIXME: used for case verification info
			for (PmCase c : cases) {

				PmCaseUtils.loadPmCaseDetails(c.getCaseDetails());
				
				for (BillingGroup bg : c.getBillingGroups()) {
					loadBillingGroup(bg);
				}
				
				if (c.getOrganizationSpecificProperties() != null) {
					c.getOrganizationSpecificProperties().getId();
				}
				
				if (c.getReferringDoctor() != null)
					c.getReferringDoctor().getName();
				
				for (PmCaseDiagnosis diag : c.getPmCaseDiagnosises()) {
					diag.getDiagnosis().getName();
					// KKORNEL: AES-13961
					if (diag.getPresentOnAdmission() != null) {
						diag.getPresentOnAdmission().getName();
					}
				}

				for (CdmCaseHistory cch : c.getCdmCaseHistories()) {
					if (cch.getAnesthesiaDepartmentLog() != null && cch.getAnesthesiaDepartmentLog().getPhaseTimeLog() != null)
						cch.getAnesthesiaDepartmentLog().getPhaseTimeLog().getBeginDate();
				}

				for (AbstractAppointment aa : c.getAbstractAppointments()) {
					aa.getBeginTime();
				}

				if (c.getPrimaryPhysician() != null)
					c.getPrimaryPhysician().getName();
				if (c.getDepartment() != null && c.getDepartment().getDepartmentType() != null)
					c.getDepartment().getDepartmentType().getName();
				for (InsurancePolicy ip : c.getOrganizationSpecificProperties().getInsurancePolicies()) {
					for (InsurancePolicy ip1 : ip.getInsurancePolicyGroup().getInsurancePolicies()) {
						if (ip1.getPolicyOwner() != null)
							ip.getPolicyOwner().getDisplayName();
					}
					for (InsurancePolicyCaseVerifiation verification : ip.getInsurancePolicyCaseVerifiations()) {
						if (verification.getPhone() != null)
							verification.getPhone().getExtension();
						if (verification.getPreApprovalPhone() != null)
							verification.getPreApprovalPhone().getExtension();
						if (verification.getInNetworkBenefits() != null)
							verification.getInNetworkBenefits().getEffectiveDate();
						if (verification.getOutNetworkBenefits() != null)
							verification.getOutNetworkBenefits().getEffectiveDate();
						if (verification.getScdOpinionPhysician() != null)
							verification.getScdOpinionPhysician().getName();
						if (verification.getReferalPhysician() != null)
							verification.getReferalPhysician().getName();
						if (verification.getVerifier() != null)
							verification.getVerifier().getName();
					}
				}

				for (InsurancePolicyCaseVerifiation ipcv : c.getInsurancePolicyCaseVerifiations()) {
					if (ipcv.getPhone() != null)
						ipcv.getPhone().getExtension();
					if (ipcv.getPreApprovalPhone() != null)
						ipcv.getPreApprovalPhone().getExtension();
				}
			}

			if (patient != null) {
				for (OrganizationSpecificProperties osp : patient.getOrganizationSpecificPropertieses()) {
					osp.getBaseOrganization().getName();
					for (InsurancePolicy ip : osp.getInsurancePolicies()) {
						for (InsurancePolicy ip1 : ip.getInsurancePolicyGroup().getInsurancePolicies()) {
							if (ip1.getPolicyOwner() != null)
								ip.getPolicyOwner().getDisplayName();
						}
						for (InsurancePolicyCaseVerifiation verification : ip.getInsurancePolicyCaseVerifiations()) {
							if (verification.getPhone() != null)
								verification.getPhone().getExtension();
							if (verification.getPreApprovalPhone() != null)
								verification.getPreApprovalPhone().getExtension();
							if (verification.getInNetworkBenefits() != null)
								verification.getInNetworkBenefits().getEffectiveDate();
							if (verification.getOutNetworkBenefits() != null)
								verification.getOutNetworkBenefits().getEffectiveDate();
							if (verification.getScdOpinionPhysician() != null)
								verification.getScdOpinionPhysician().getName();
							if (verification.getReferalPhysician() != null)
								verification.getReferalPhysician().getName();
							if (verification.getVerifier() != null)
								verification.getVerifier().getName();
						}
					}
				}
			}

			if (DepartmentManager.isSCLike(job.getModuleDepartment())) {
				String typeOfBill = (String)sm.execute("SELECT c.typeOfBill " +
						"FROM Charge c " + 
						"WHERE c.typeOfBill IS NOT NULL " + 
						"AND c.patient=:patient " + 
						"ORDER BY c.lastEditDate DESC")
						.setParameter("patient", patient)
						.setMaxResults(1).uniqueResult();
				result.setTypeOfBill(typeOfBill);
			}

			// aging
			Aging aging = AgingCalculatorManager.calculate(sm, job.getModuleDepartment(), patient.getAccount(), false);
			result.setAging(aging);

			result.setPatient(patient);
			// getMostRecentCase
			// getAppointmentsByCase
		} catch (Exception ex) {
			result.setException(ex);
			logError(ex);
		} finally {
			sm.closeSession();
		}
		log.debug(String.format("Select Patient runtime: %d msec", System.currentTimeMillis()-start));
		return result;
	}
	
	private void logError(Exception ex) {
		if (!(ex instanceof AOException))
			log.error(TITLE, ex);
	}
}

/*
		// comes from T&M
		Collections.sort(l, new Comparator<TimeAndMaterialWorksheetRow>() {

			private List<PhaseKind> phaseKindOrder = Arrays.asList(new PhaseKind[] {PhaseKind.PAT, PhaseKind.PREOPERATIVE, PhaseKind.ANESTHESIA, PhaseKind.OPERATIVE, PhaseKind.PACU, PhaseKind.STEPDOWN});
			private List<CaseCostingRowKind> typeOrder = Arrays.asList(new CaseCostingRowKind[] {CaseCostingRowKind.PROCEDURE, CaseCostingRowKind.TRAY_ITEM, CaseCostingRowKind.SUPPLY_RESOURCE, CaseCostingRowKind.CDM_DEPARTMENT});
			
			@Override
			public int compare(TimeAndMaterialWorksheetRow row1, TimeAndMaterialWorksheetRow row2) {
				if (row1.getPhaseKind() != row2.getPhaseKind()) {
					return phaseKindOrder.indexOf(row1.getPhaseKind())-phaseKindOrder.indexOf(row2.getPhaseKind());
				}
				
				if (row1.getKind() != row2.getKind()) {
					return typeOrder.indexOf(row1.getKind())-typeOrder.indexOf(row2.getKind());
				}
				
				if (row1.getSequence() == null && row2.getSequence() != null)
					return 1;
				
				if (row1.getSequence() != null && row2.getSequence() != null)
					return row1.getSequence().compareTo(row2.getSequence());
				
				return -1;
			}
		});
*/